home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World's Largest Collection of Windows Software
/
The World's Largest Collection of Windows Software - Disc 2.iso
/
games
/
_b2
/
winmaze4
/
maze3d.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-16
|
28KB
|
850 lines
#include <owl\owlpch.h>
#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dc.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "oracle.h"
#include "cell.h"
#include "hexmaze.h"
#include "sqrmaze.h"
#include "plot3d.h"
#include "maze3d.rh"
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static int hex_external_to_plot(double,double);
static double hex_f(double,double);
static int hex_red(double,double);
static int sqr_external_to_plot(double,double);
static double sqr_f(double,double);
static int sqr_red(double,double);
// maze constants
#define PIXELS_PER_HEXAGONAL_ROOM 60
#define PIXELS_PER_SQUARE_ROOM 40
#define RESOLUTION 4 // larger values takes more time (and
// virtual memory) but produce a better
// image.
hexmaze *hexmaze_ptr; // pointer to generator for maze with hexagonal rooms
sqrmaze *sqrmaze_ptr; // pointer to generator for maze with square rooms
class MazeWindow : public TFrameWindow
{
public:
MazeWindow(TWindow* parent,const char far* title);
void CleanupWindow();
BOOL IdleAction(long IdleCount);
// So that the program remains responsive, the plotting is done here.
void Paint(TDC &dc,BOOL erase,TRect &dirty);
// This method is invoked when part of the display has been made
// "dirty" (for example, when another window has been moved from
// atop the display).
protected:
void CeActionClear(TCommandEnabler &ce);
// Determines whether the action "Clear" may be selected.
void CeActionSolve(TCommandEnabler &ce);
// Determines whether the action "Solve" may be selected.
void CeStyleSquare(TCommandEnabler &ce);
// Determines whether a check mark should appear to the left of the
// style "Square rooms".
void CeStyleHexagon(TCommandEnabler &ce);
// Determines whether a check mark should appear to the left of the
// style "Hexagonal rooms".
void CmActionNew();
// Processes a request for a new maze.
void CmActionSolve();
// Processes a request that a maze be solved.
void CmActionClear();
// Processes a request that the solution be cleared from a maze.
void CmHelpAbout();
// Processes a request for information about the author.
void CmHelpUsage();
// Processes a request for information about using this program.
void CmStyleSquare();
// Processes a request for square rooms.
void CmStyleHexagon();
// Processes a request for hexagonal rooms.
void EvHScroll(UINT code,UINT pos,HWND wnd);
// Processes a change in the horizontal scroll bar -- a request for
// a new angle of rotation.
void EvSize(UINT sizeType,TSize &size);
// Processes resizing of the window -- a request for a different size
// maze.
void EvVScroll(UINT code,UINT pos,HWND wnd);
// Processes a change in the vertical scroll bar -- a request for a
// new angle of tilt.
private:
void DisplayMessage(const string& msg);
// Pops up a message to the user.
double light_x;
double light_y;
double light_z;
// Vector to the major source of light.
int num_columns;
// Number of columns in the current maze.
int num_rows;
// Number of rows in the current maze.
int only_plot_solution;
// If TRUE, only plot the outline to the solution of the maze.
plot3d *plot3d_ptr;
// Pointer to an instance of the 3D plotting class.
TRect region_to_paint;
// The part of the maze to be plotted.
int rotation;
// The number of degrees the maze is rotated about a line
// perpendicular to its base.
char seed [9];
// Random number seed (derived from the system clock) used to
// generate the maze.
int solve;
// TRUE if the maze is to be solved.
int solved;
// TRUE if the solution is currently displayed.
char state;
// State of the plotting; one of the following:
// 'B' -- beginning
// 'M' -- maze being generated
// 'S' -- preparing plot
// 'P' -- plotting
// 'F' -- failure
// 'D' -- done
char style;
// 'H' for hexagonal rooms; 'S' for square rooms.
int tilt;
// The number of degrees the maze is tilted towards the viewer.
DECLARE_RESPONSE_TABLE(MazeWindow);
// Associates user commands with methods in this program.
};
DEFINE_RESPONSE_TABLE1(MazeWindow,TFrameWindow)
EV_COMMAND(CM_ACTION_NEW,CmActionNew),
EV_COMMAND(CM_ACTION_SOLVE,CmActionSolve),
EV_COMMAND(CM_ACTION_CLEAR,CmActionClear),
EV_COMMAND(CM_HELP_ABOUT,CmHelpAbout),
EV_COMMAND(CM_HELP_USAGE,CmHelpUsage),
EV_COMMAND(CM_STYLE_SQUARE,CmStyleSquare),
EV_COMMAND(CM_STYLE_HEXAGON,CmStyleHexagon),
EV_COMMAND_ENABLE(CM_ACTION_SOLVE,CeActionSolve),
EV_COMMAND_ENABLE(CM_ACTION_CLEAR,CeActionClear),
EV_COMMAND_ENABLE(CM_STYLE_SQUARE,CeStyleSquare),
EV_COMMAND_ENABLE(CM_STYLE_HEXAGON,CeStyleHexagon),
EV_WM_HSCROLL,
EV_WM_VSCROLL,
EV_WM_SIZE,
END_RESPONSE_TABLE;
MazeWindow::MazeWindow(
TWindow *parent,
const char far *title) : TFrameWindow(parent, title)
{
Attr.Style |= WS_VSCROLL | WS_HSCROLL;
// Scroll bars are used to specify tilt and rotation.
AssignMenu("MAZE_MENU");
state='B'; // beginning plot
style='S'; // square rooms
rotation=0; // (degrees)
tilt=30; // (degrees)
light_x=(double) 1.5; // vector to light source
light_y=(double) -1.0;
light_z=(double) 2.6;
solve=FALSE; // initially, don't show the solution
plot3d_ptr=new plot3d((TFrameWindow *) this); // 3D plotter
}
void MazeWindow::CleanupWindow()
{
delete plot3d_ptr; // destroy 3D plotter
TFrameWindow::CleanupWindow();
}
void MazeWindow::DisplayMessage(const string &msg)
// Pops up a message to the user.
{
MessageBox(msg.c_str(),GetApplication()->GetName(),
MB_OK | MB_ICONEXCLAMATION);
return;
}
BOOL MazeWindow::IdleAction(long IdleCount)
// So that the program remains responsive, the plotting is done here.
{
switch (state)
// State of the plotting; one of the following:
// 'B' -- beginning
// 'R' -- restarting
// 'M' -- maze being generated
// 'S' -- preparing plot
// 'P' -- plotting
// 'F' -- failure
// 'D' -- done
{
case 'B': // begin
{
solved=FALSE;
// The solution is not (or soon won't be) completely displayed.
GetClientRect(region_to_paint);
// The whole maze is being plotted.
SetScrollRange(SB_VERT,0,90);
// The maze may be tilted between 0 and 90 degrees.
SetScrollRange(SB_HORZ,0,360);
// The maze may be rotated between 0 and 360 degrees.
SetScrollPos(SB_VERT,90-tilt);
// Display the current tilt on the vertical scroll bar.
SetScrollPos(SB_HORZ,rotation);
// Display the current rotation on the horizontal scroll bar.
only_plot_solution=FALSE;
// Plot the base, walls, etc.
// Pick a maze at random.
time_t quotient;
time_t remainder;
time_t start_time;
time(&start_time);
for (int i=0; i < 8; i++)
{
quotient=start_time/10;
remainder=start_time-10*quotient;
seed[i]=char('0'+remainder);
start_time=quotient;
}
// Since the width of a room (in millimeters) is constant,
// the number of rows and columns in a maze is determined by
// the current size of the (client) window.
TClientDC *dc=new TClientDC(*this);
double aspect_ratio
=(double(dc->GetDeviceCaps(VERTSIZE))
/double(dc->GetDeviceCaps(HORZSIZE)))
/(double(dc->GetDeviceCaps(VERTRES))
/double(dc->GetDeviceCaps(HORZRES)));
if (style == 'S') // square rooms
{
num_columns=(GetClientRect().Width())/PIXELS_PER_SQUARE_ROOM;
num_rows=(int) ((2.0/sqrt(3.0))*aspect_ratio
*double(GetClientRect().Height())
/double(PIXELS_PER_SQUARE_ROOM));
}
else // hexagonal rooms
{
num_columns=(2*GetClientRect().Width())
/(3*PIXELS_PER_HEXAGONAL_ROOM)-1;
num_columns=2*num_columns+1;
num_rows=(int) ((2.0/sqrt(3.0))*aspect_ratio
*double(GetClientRect().Height())
/double(PIXELS_PER_HEXAGONAL_ROOM));
}
delete dc;
}
if ((num_columns >= 4) && (num_rows >= 4))
// The maze generator requires at least 4 rows and 4 columns.
{
state='M';
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
// Clear the (client) window.
dc->TextOut(0,0,"Generating...");
// Let the user know the maze is being generated.
delete dc;
}
else
// If the window is too small for a maze with 4 rows or 4 columns,
// let the user know.
{
state='F';
if (! IsIconic())
{
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
DisplayMessage("You need a larger window for a maze!");
}
}
break;
case 'M': // Generate maze.
{
HCURSOR oldCursor=::SetCursor(::LoadCursor(0,IDC_WAIT));
if (style == 'S') // square rooms
sqrmaze_ptr
=new sqrmaze(num_rows,num_columns,RESOLUTION,&seed[0],this);
else // hexagonal rooms
hexmaze_ptr
=new hexmaze(num_rows,num_columns,RESOLUTION,&seed[0],this);
::SetCursor(oldCursor);
}
if (style == 'S') // square rooms
if (sqrmaze_ptr->constructed())
state='S'; // proceed to prepare plot
else
{
state='F'; // failure (already announced in pop up)
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
else // hexagonal rooms
if (hexmaze_ptr->constructed())
state='S'; // proceed to prepare plot
else
{
state='F'; // failure (already announced in pop up)
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
break;
case 'R': // Restart.
solved=FALSE;
// The solution is not (or soon won't be) completely displayed.
GetClientRect(region_to_paint);
// The whole maze is being plotted.
SetScrollRange(SB_VERT,0,90);
// The maze may be tilted between 0 and 90 degrees.
SetScrollRange(SB_HORZ,0,360);
// The maze may be rotated between 0 and 360 degrees.
SetScrollPos(SB_VERT,90-tilt);
// Display the current tilt on the vertical scroll bar.
SetScrollPos(SB_HORZ,rotation);
// Display the current rotation on the horizontal scroll bar.
only_plot_solution=FALSE;
// Plot the base, walls, etc.
if (style == 'S') // square rooms
if (sqrmaze_ptr->constructed())
state='S'; // proceed to prepare plot
else
{
state='F'; // failure (already announced in pop up)
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
else // hexagonal rooms
if (hexmaze_ptr->constructed())
state='S'; // proceed to prepare plot
else
{
state='F'; // failure (already announced in pop up)
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
break;
case 'S': // prepare plot
if (style == 'S') // square rooms
switch (plot3d_ptr->prepare_plot(
sqr_f,sqrmaze_ptr->x_min(),sqrmaze_ptr->x_max(),
sqrmaze_ptr->y_min(),sqrmaze_ptr->y_max(),sqr_external_to_plot,
sqr_red,sqrmaze_ptr->num_x_divisions(),
sqrmaze_ptr->num_y_divisions(),double(rotation),double(tilt),
light_x,light_y,light_z))
{
case 'S': // success; plot prepared
state='P'; // proceed to plot maze
break;
case 'F': // failure (already announced in pop up)
{
state='F';
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
break;
default: // continue preparing plot
break;
}
else // hexagonal rooms
switch (plot3d_ptr->prepare_plot(
hex_f,hexmaze_ptr->x_min(),hexmaze_ptr->x_max(),
hexmaze_ptr->y_min(),hexmaze_ptr->y_max(),hex_external_to_plot,
hex_red,hexmaze_ptr->num_x_divisions(),
hexmaze_ptr->num_y_divisions(),double(rotation),double(tilt),
light_x,light_y,light_z))
{
case 'S': // success; plot prepared
state='P'; // proceed to plot maze
break;
case 'F': // failure (already announced in pop up)
{
state='F';
TClientDC *dc=new TClientDC(*this);
dc->PatBlt(this->GetClientRect(),WHITENESS);
delete dc;
}
break;
default: // continue preparing plot
break;
}
break;
case 'P': // plotting
switch (plot3d_ptr->plot(
region_to_paint,
solve, // highlight solution
only_plot_solution)) // only plot solution
{
case 'S': // success; plot complete
solved=solve;
state='D'; // proceed to wait for user input
break;
case 'F': // failure (already announced in pop up)
solved=FALSE;
{
state='F';
TClientDC *dc=new TClientDC(*this);
delete dc;
}
break;
default: // continue
break;
}
break;
case 'F': // failed; wait for user input
break;
default: // done; wait for user input
break;
}
TFrameWindow::IdleAction(IdleCount);
return TRUE;
}
void MazeWindow::Paint(TDC &dc,BOOL erase,TRect &dirty)
// This method is invoked when part of the display has been made "dirty"
// (for example, when another window has been moved from atop the display).
{
only_plot_solution=FALSE;
// the walls, etc. around the solution are probably dirty too
switch (state)
{
case 'B': // beginning
// Nothing is displayed yet; nothing need be restored.
break;
case 'M': // generating maze
dc.PatBlt(this->GetClientRect(),WHITENESS);
dc.TextOut(0,0,"Generating...");
// Redisplay the message.
break;
case 'S': // preparing plot
dc.PatBlt(this->GetClientRect(),WHITENESS);
dc.TextOut(0,0,"Preparing plot...");
// The internal state of the 3D plotter is not available here,
// so display this message.
break;
case 'P': // still plotting
GetClientRect(region_to_paint);
state='P';
plot3d_ptr->restart_plot();
// Proceed to restore the entire display. Because the painter's
// algorithm is used, areas outside the "dirty" region may still
// need plotting.
break;
case 'F': // failed
// Nothing is being displayed; nothing need be restored.
break;
default: // done
state='P';
region_to_paint=dirty;
plot3d_ptr->restart_plot();
// Redo the "dirty" region.
break;
}
}
void MazeWindow::EvHScroll(UINT code,UINT pos,HWND wnd)
// Processes a change in the horizontal scroll bar -- a request for a new angle
// of rotation.
{
TFrameWindow::EvHScroll(code,pos,wnd);
int change=FALSE;
switch (code)
{
case SB_LINELEFT:
rotation--;
if (rotation < 0)
rotation=0;
change=TRUE;
break;
case SB_LINERIGHT:
rotation++;
if (rotation > 360)
rotation=360;
change=TRUE;
break;
case SB_PAGELEFT:
rotation-=10;
if (rotation < 0)
rotation=0;
change=TRUE;
break;
case SB_PAGERIGHT:
rotation+=10;
if (rotation > 360)
rotation=360;
change=TRUE;
break;
case SB_THUMBPOSITION:
rotation=pos;
change=TRUE;
break;
case SB_THUMBTRACK:
break;
default:
break;
}
if (change)
{
delete plot3d_ptr;
state='R'; // proceed to completely redo plot
plot3d_ptr=new plot3d((TFrameWindow *) this);
}
return;
}
void MazeWindow::EvSize(UINT sizeType,TSize &size)
// Processes resizing of the window -- a request for a different size maze.
{
// get rid of old maze
if (style == 'S') // square rooms
{
delete sqrmaze_ptr;
sqrmaze_ptr=NULL;
}
else // hexagonal rooms
{
delete hexmaze_ptr;
hexmaze_ptr=NULL;
}
delete plot3d_ptr; // get rid of old 3D plotter
solve=FALSE; // initially, don't display the solution
state='B'; // proceed to do new maze
plot3d_ptr=new plot3d((TFrameWindow *) this);
return;
}
void MazeWindow::EvVScroll(UINT code,UINT pos,HWND wnd)
// Processes a change in the vertical scroll bar -- a request for a new angle of
// tilt.
{
TFrameWindow::EvVScroll(code,pos,wnd);
int change=FALSE;
switch (code)
{
case SB_LINEUP:
tilt++;
if (tilt > 90)
tilt=90;
change=TRUE;
break;
case SB_LINEDOWN:
tilt--;
if (tilt < 0)
tilt=0;
change=TRUE;
break;
case SB_PAGEUP:
tilt+=5;
if (tilt > 90)
tilt=90;
change=TRUE;
break;
case SB_PAGEDOWN:
tilt-=5;
if (tilt < 0)
tilt=0;
change=TRUE;
break;
case SB_THUMBPOSITION:
tilt=90-pos;
change=TRUE;
break;
case SB_THUMBTRACK:
break;
default:
break;
}
if (change)
{
delete plot3d_ptr;
state='R'; // proceed to completely redo plot
plot3d_ptr=new plot3d((TFrameWindow *) this);
}
return;
}
void MazeWindow::CmActionNew()
// Processes a request for a new maze.
{
// get rid of old maze
if (style == 'S') // square rooms
{
delete sqrmaze_ptr;
sqrmaze_ptr=NULL;
}
else // hexagonal rooms
{
delete hexmaze_ptr;
hexmaze_ptr=NULL;
}
delete plot3d_ptr; // get rid of old 3D plotter
solve=FALSE; // initially, don't display the solution
state='B'; // proceed to do new maze
plot3d_ptr=new plot3d((TFrameWindow *) this);
return;
}
void MazeWindow::CeActionSolve(TCommandEnabler& ce)
// Determines whether the action "Solve" may be selected.
{
ce.Enable((state == 'D') && (! solved));
// "Solve" may be selected if the plotting is done and the maze has
// not yet been solved.
}
void MazeWindow::CmActionSolve()
// Processes a request that a maze be solved.
{
if (state == 'D') // plotting done
{
if (! solved) // not solved yet
{
only_plot_solution=TRUE; // only the solution need be plotted
solve=TRUE;
plot3d_ptr->restart_plot(); // set the 3D plotter to restart
GetClientRect(region_to_paint); // whole solution will be plotted
state='P'; // proceed to plot the solution
}
}
return;
}
void MazeWindow::CeActionClear(TCommandEnabler& ce)
// Determines whether the action "Clear" may be selected.
{
ce.Enable((state == 'D') && (solved));
// "Clear" may be selected if the plotting is done and the solution is
// currently displayed.
}
void MazeWindow::CmActionClear()
// Processes a request that the solution be cleared from a maze.
{
if (state == 'D') // plotting done
{
if (solved) // solution displayed
{
only_plot_solution=TRUE; // only the solution need be cleared
solve=FALSE;
plot3d_ptr->restart_plot(); // set the 3D plotter to restart
GetClientRect(region_to_paint); // whole solution will be cleared
state='P'; // proceed to clear the solution
}
}
return;
}
void MazeWindow::CmHelpAbout()
// Processes a request for information about the author.
{
MessageBox("\tMAZE3D\n\n"
"Written on May 2, 1994 by\n"
"\tJames L. Dean\n"
"\t406 40th Street\n"
"\tNew Orleans, LA 70124-1532\n"
"\tcsvcjld@nomvs.lsumc.edu\n",
GetApplication()->GetName(),MB_ICONINFORMATION);
return;
}
void MazeWindow::CmHelpUsage()
// Processes a request for information about using this program.
{
MessageBox("\t\tMAZE3D\n\n"
" The following options are available under \"Action\":\n"
"\tNew -- generate another maze\n"
"\tSolve -- outline the solution\n"
"\tClear -- remove the solution\n\n"
" Under \"Style\" you may select square or\n"
"hexagonal rooms.\n\n"
" Use the horizontal scroll bar to vary the\n"
"rotation form 0 to 360 degrees.\n\n"
" Use the vertical scroll bar to vary the tilt from\n"
"0 to 90 degrees.\n\n"
" Resizing the window yields a new maze. The size\n"
"of the rooms remains the same.",
GetApplication()->GetName(),MB_ICONINFORMATION);
return;
}
void MazeWindow::CeStyleSquare(TCommandEnabler& ce)
// Determines whether a check mark should appear to the left of the style
// "Square rooms".
{
ce.SetCheck(style == 'S');
}
void MazeWindow::CmStyleSquare()
// Processes a request for square rooms.
{
if (style != 'S') // style is currently hexagonal rooms
{
delete hexmaze_ptr; // get rid of maze generator for hexagonal rooms
hexmaze_ptr=NULL;
delete plot3d_ptr; // get rid of the current 3D plotter
style='S'; // style is now square rooms
solve=FALSE; // initially, don't display the solution
state='B'; // proceed to do new maze
plot3d_ptr=new plot3d((TFrameWindow *) this);
}
return;
}
void MazeWindow::CeStyleHexagon(TCommandEnabler& ce)
// Determines whether a check mark should appear to the left of the style
// "Hexagonal rooms".
{
ce.SetCheck(style == 'H');
}
void MazeWindow::CmStyleHexagon()
{
// Processes a request for hexagonal rooms.
if (style != 'H') // style is currently square rooms
{
delete sqrmaze_ptr; // get rid of maze generator for square rooms
sqrmaze_ptr=NULL;
delete plot3d_ptr; // get rid of current 3D plotter
style='H'; // style is now square rooms
solve=FALSE; // initially, don't display the solution
state='B'; // proceed to do new maze
plot3d_ptr=new plot3d((TFrameWindow *) this);
}
return;
}
static int hex_external_to_plot(
double x,
double y)
// Returns TRUE if and only if a point is external to the maze with hexagonal
// rooms.
{
return hexmaze_ptr->external_to_maze(x,y);
}
static int hex_red(
double x,
double y)
// Returns TRUE if and only if a point lies on top of a wall adjacent to the
// solution of the maze with hexagonal rooms.
{
return hexmaze_ptr->part_of_solution(x,y);
}
static double hex_f(
double x,
double y)
// Returns a positive value for a point on top of a wall of the maze with
// hexagonal rooms, 0 otherwise.
{
return hexmaze_ptr->f(x,y);
}
static int sqr_external_to_plot(
double x,
double y)
// Returns TRUE if and only if a point is external to the maze with square
// rooms.
{
return sqrmaze_ptr->external_to_maze(x,y);
}
static int sqr_red(
double x,
double y)
// Returns TRUE if and only if a point lies on top of a wall adjacent to the
// solution of the maze with square rooms.
{
return sqrmaze_ptr->part_of_solution(x,y);
}
static double sqr_f(
double x,
double y)
// Returns a positive value for a point on top of a wall of the maze with
// square rooms, 0 otherwise.
{
return sqrmaze_ptr->f(x,y);
}
class MazeApp : public TApplication
{
public:
MazeApp() : TApplication("3D Mazes") {}
void InitMainWindow();
};
void MazeApp::InitMainWindow()
{
MainWindow=new MazeWindow(0,"3D Mazes");
MainWindow->SetIcon(this,"MAZE_ICON");
}
int OwlMain(int /*argc*/, char* /*argv*/ [])
// Execution of this program starts here.
{
hexmaze_ptr=NULL; // Initialize pointers to the maze generators, one
sqrmaze_ptr=NULL; // for hexagonal rooms and one for square rooms.
int rc=MazeApp().Run(); // Process Windows messages until user is done.
delete sqrmaze_ptr; // Get rid of any maze generators.
delete hexmaze_ptr;
return rc;
}